﻿using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Domain.Directory;
using Nop.Services.Customers;
using Nop.Services.Events;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Nop.Services.Directory
{
    public partial class HolidayService : IHolidayService
    {
        #region Constants

        private const string HOLIDAYS_ALL_KEY = "Nop.holiday.all";
        private const string HOLIDAYS_PATTERN_KEY = "Nop.holiday.";

        #endregion

        #region Fields

        private readonly IRepository<Holiday> _holidayRepository;
        private readonly IRepository<HolidayDate> _holidayDateRepository;
        private readonly ICacheManager _cacheManager;
        private readonly IEventPublisher _eventPublisher;

        #endregion

        #region Ctor

        public HolidayService(
            IRepository<Holiday> holidayRepository,
            IRepository<HolidayDate> holidayDateRepository,
            ICacheManager cacheManager,
            IEventPublisher eventPublisher
            )
        {
            this._holidayRepository = holidayRepository;
            this._holidayDateRepository = holidayDateRepository;
            this._cacheManager = cacheManager;
            this._eventPublisher = eventPublisher;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets all holidays
        /// </summary>
        /// <returns>holiday collection</returns>
        public virtual IList<Holiday> GetAllHolidays()
        {
            const string key = HOLIDAYS_ALL_KEY;
            return _cacheManager.Get(key, () =>
            {
                var query = _holidayRepository.Table;
                query = query.OrderBy(h => h.DisplayOrder);
                var holidays = query.ToList();
                return holidays;
            },1440);
        }

        /// <summary>
        /// Deletes a holiday
        /// </summary>
        /// <param name="holiday">holiday</param>
        public virtual void DeleteHoliday(Holiday holiday)
        {
            if (holiday == null)
                throw new ArgumentNullException("holiday");

            _holidayRepository.Delete(holiday);

            //cache
            _cacheManager.RemoveByPattern(HOLIDAYS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityDeleted(holiday);
        }

        /// <summary>
        /// Gets a holiday
        /// </summary>
        /// <param name="holidayId">Holiday identifier</param>
        /// <returns>Holiday</returns>
        public virtual Holiday GetHolidayById(int holidayId)
        {
            if (holidayId == 0)
                return null;

            return GetAllHolidays().SingleOrDefault(h => h.Id == holidayId);
        }

        /// <summary>
        /// Get the holiday information by date
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public virtual Holiday GetHolidayByDate(DateTime date)
        {
            return GetAllHolidays().FirstOrDefault(h => h.Dates.Any(d => d.Date == date.Date));
        }

        /// <summary>
        /// Inserts a holiday
        /// </summary>
        /// <param name="holiday">Holiday</param>
        public virtual void InsertHoliday(Holiday holiday)
        {
            if (holiday == null)
                throw new ArgumentNullException("holiday");

            _holidayRepository.Insert(holiday);

            //cache
            _cacheManager.RemoveByPattern(HOLIDAYS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityInserted(holiday);
        }

        /// <summary>
        /// Updates a holiday
        /// </summary>
        /// <param name="holiday">Holiday</param>
        public virtual void UpdateHoliday(Holiday holiday)
        {
            if (holiday == null)
                throw new ArgumentNullException("holiday");

            //update holiday
            _holidayRepository.Update(holiday);

            //cache
            _cacheManager.RemoveByPattern(HOLIDAYS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityUpdated(holiday);
        }

        /// <summary>
        /// Check if the date is holiday
        /// </summary>
        /// <param name="date">Date to check</param>
        /// <param name="stateProvinceId">state province id</param>
        /// <returns></returns>
        public virtual bool IsHoliday(DateTime date, int? stateProvinceId = null)
        {
            return GetAllHolidays().Any(
                h => (!stateProvinceId.HasValue || h.StateProvinces.Any(sp => sp.Id == stateProvinceId)) 
                    && h.Dates.Any(d => d.Date.Date == date.Date)
                    );
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="holidayId"></param>
        /// <returns></returns>
        public virtual List<DateTime> GetDatesById(int holidayId)
        {
            var holiday = GetHolidayById(holidayId);
            if (holiday == null)
            {
                return null;
            }

            return holiday.Dates.Select(hd => hd.Date).ToList();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        public virtual void DeleteDateById(int id)
        {
            var holidayDate = _holidayDateRepository.GetById(id);
            if (holidayDate == null)
                throw new ArgumentNullException("id");

            _holidayDateRepository.Delete(holidayDate);

            //cache
            _cacheManager.RemoveByPattern(HOLIDAYS_PATTERN_KEY);

            //TODO: Add new event
            //event notification
            _eventPublisher.EntityDeleted(holidayDate.Holiday);
        }

        #endregion
    }
}
